package main.java.DMDE.view;

import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.TooManyListenersException;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.xml.parsers.ParserConfigurationException;

import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
import com.mxgraph.io.mxCodec;
import com.mxgraph.swing.mxGraphComponent;
import com.mxgraph.util.mxCellRenderer;
import com.mxgraph.util.mxResources;
import com.mxgraph.util.mxXmlUtils;
import com.mxgraph.util.png.mxPngEncodeParam;
import com.mxgraph.util.png.mxPngImageEncoder;
import com.mxgraph.view.mxGraph;

import main.java.DMDE.controllers.IDiagramController;
import main.java.ERMDE.infrastructure.IterableExtensions;
import main.java.DMDE.model.Entity;

public class DiagramView extends JPanel implements IDiagramView,
	DropTargetListener {

    private static final long serialVersionUID = 1L;
    private IDiagramController diagramController;
    private final JButton btnEntity;
    private final JButton btnRelationship;
    private final JButton btnSave;
    private final JButton btnSubdiagram;
    private mxGraphComponent graphComponent;
    private JPopupMenu entityMenu;
    private final JButton btnPrint;
    private final JButton btnExport;
    private JButton btnZoomIn;
    private JButton btnZoomOut;

    public DiagramView() {
	
	setLayout(new FormLayout(new ColumnSpec[] {
		FormFactory.RELATED_GAP_COLSPEC, FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.RELATED_GAP_COLSPEC,
		FormFactory.RELATED_GAP_COLSPEC,FormFactory.DEFAULT_COLSPEC,FormFactory.DEFAULT_COLSPEC,
		}, new RowSpec[] {
		FormFactory.RELATED_GAP_ROWSPEC, FormFactory.DEFAULT_ROWSPEC,
		FormFactory.RELATED_GAP_ROWSPEC,
		RowSpec.decode("default:grow"), }));

	this.btnEntity = new JButton("Entity");
	add(this.btnEntity, "2, 2");

	this.btnRelationship = new JButton("Relationship");
	add(this.btnRelationship, "5, 2");

	this.btnSave = new JButton("Save");
	add(this.btnSave, "8, 2");

	this.btnSubdiagram = new JButton("Sub-Diagram");
	add(this.btnSubdiagram, "11, 2");

	this.btnPrint = new JButton("Print");
	add(this.btnPrint, "14, 2");

	this.btnExport = new JButton("Export");
	add(this.btnExport, "17, 2");

	this.btnZoomIn = new JButton("+");
	add(btnZoomIn, "20, 2");

	this.btnZoomOut = new JButton("-");
	add(btnZoomOut, "23, 2");

    }

    private void initializeEntityMenu() {
	this.entityMenu = new JPopupMenu();

	this.entityMenu.add(new JMenuItem(new AbstractAction("New Class") {
	    private static final long serialVersionUID = 1L;

	    @Override
	    public void actionPerformed(ActionEvent e) {
		String name = newEntityName();
		if (name != null && !(name.equals("")))
		    diagramController.createClass(name);
	    }
	}));

	this.entityMenu.add(new JMenuItem(new AbstractAction("New Interface") {
	    private static final long serialVersionUID = 1L;

	    @Override
	    public void actionPerformed(ActionEvent e) {
		String name = newEntityName();
		if (name != null && !(name.equals("")))
		    diagramController.createInterface(name);
	    }
	}));
	
	this.btnEntity.addMouseListener(new MouseAdapter() {
	    @Override
	    public void mouseClicked(MouseEvent e) {
		entityMenu.show(e.getComponent(), btnEntity.getX(),
			btnEntity.getY() + btnEntity.getHeight());

	    }
	});
	
	this.btnRelationship.addMouseListener(new MouseAdapter() {
		@Override
		public void mouseClicked(MouseEvent e) {
			diagramController.createRelationship();
		}
	});
	
	this.btnSubdiagram.addMouseListener(new MouseAdapter() {
	    @Override
	    public void mouseClicked(MouseEvent e) {
		String diagramName = JOptionPane.showInputDialog(null,
			"Provide the diagram's name", "New Diagram",JOptionPane.QUESTION_MESSAGE);
		if (diagramName != null) {
		    diagramController.createSubDiagram(diagramName);
		}
	    }
	});
	this.btnPrint.addActionListener(new PrintAction());
	this.btnExport.addActionListener(new ExportAction());
	
	this.btnZoomIn.addMouseListener(new MouseAdapter() {
		@Override
		public void mouseClicked(MouseEvent e) {
			graphComponent.zoomIn();
		}
	});

	this.btnZoomOut.addMouseListener(new MouseAdapter() {
		@Override
		public void mouseClicked(MouseEvent e) {
			graphComponent.zoomOut();
		}
	});
	
	this.btnSave.addMouseListener(new MouseAdapter() {
		@Override
		public void mouseClicked(MouseEvent e) {
			try {
				diagramController.save();
				JOptionPane.showMessageDialog(null, "Diagram saved",
						"Save Diagram", JOptionPane.INFORMATION_MESSAGE);
			} catch (ParserConfigurationException exception) {
				exception.printStackTrace();
			}
		}
	});
    }

    private String newEntityName() {
	String projectName = JOptionPane.showInputDialog(null,
		"Provide the entity's name", "New Entity",
		JOptionPane.QUESTION_MESSAGE);
	return projectName;
    }

     @Override
    public void setController(IDiagramController controller) {
	this.diagramController = controller;

	this.graphComponent = new mxGraphComponent(
		this.diagramController.getGraph());
	this.graphComponent.setAutoScroll(true);
	this.graphComponent.setAutoscrolls(true);
	this.graphComponent.getConnectionHandler().setCreateTarget(false);
	this.graphComponent.setConnectable(false);

	MouseListener listener = new MouseAdapter() {
	    @Override
	    public void mouseClicked(MouseEvent e) {
		if (diagramController.hasPendingClass()) {
		    try {
			diagramController.addClass(e.getPoint().x,
				e.getPoint().y);
		    } catch (Exception e1) {
			e1.printStackTrace();
		    }
		}
		if (diagramController.hasPendingInterface()) {
		    try {
			diagramController.addInterface(e.getPoint().x,
				e.getPoint().y);
		    } catch (Exception e1) {
			e1.printStackTrace();
		    }
		}    
	    }
	};
	this.graphComponent.getGraphControl().addMouseListener(listener);

	try {
	    this.graphComponent.getDropTarget().addDropTargetListener(this);
	} catch (TooManyListenersException e1) {
	    // should not occur
	    e1.printStackTrace();
	}
	this.add(this.graphComponent, "2, 4, 30, 1, fill, fill");
	
	initializeEntityMenu();
	
    }

    @Override
    public boolean showDeleteDialog(String typeAndName, String otherMessege,
	    boolean couldDelete) {
	int result = -1;
	if (couldDelete == false)
	    JOptionPane.showMessageDialog(null, "The selected " + typeAndName
				+ otherMessege, "Deleting invalid " + typeAndName,
				JOptionPane.QUESTION_MESSAGE);
	else
	    result = JOptionPane.showConfirmDialog(null, "The " + typeAndName
				+ " are being deleted, are you shure you want this?",
				"Deleting " + typeAndName, JOptionPane.YES_NO_OPTION);

	if (result == JOptionPane.YES_OPTION) {
		return true;
	}
	return false;
    }

    @Override
    public void refreshGraphComponent() {
	this.graphComponent.refresh();
    }

    @Override
    public void dragEnter(DropTargetDragEvent dtde) {
	// TODO Auto-generated method stub

    }

    @Override
    public void dragExit(DropTargetEvent dte) {
	// TODO Auto-generated method stub

    }

    @Override
    public void dragOver(DropTargetDragEvent dtde) {
	// TODO Auto-generated method stub

    }

    @Override
    public void drop(DropTargetDropEvent dtde) {
	// TODO Auto-generated method stub

    }

    @Override
    public void dropActionChanged(DropTargetDragEvent dtde) {
	// TODO Auto-generated method stub

    }
    
    private class PrintAction implements ActionListener {

	@Override
	public void actionPerformed(ActionEvent e) {
	    PrinterJob pj = PrinterJob.getPrinterJob();
	    if (pj.printDialog()) {
		PageFormat pf = graphComponent.getPageFormat();
		Paper paper = new Paper();
		double margin = 36;
		paper.setImageableArea(margin, margin, paper.getWidth()
			- margin * 2, paper.getHeight() - margin * 2);
		pf.setPaper(paper);
		pj.setPrintable(graphComponent, pf);
		try {
		    pj.print();
		} catch (PrinterException e2) {
		    System.out.println(e2);
		}
	    }
	}
    }

    private class ExportAction implements ActionListener {

	public void actionPerformed(ActionEvent e) {
	    mxGraph graph = graphComponent.getGraph();
	    // Creates the image for the PNG file
	    BufferedImage image = mxCellRenderer.createBufferedImage(graph,
				null, 1, graphComponent.getBackground(),
				graphComponent.isAntiAlias(), null,graphComponent.getCanvas());
	    // Creates the URL-encoded XML data
	    mxCodec codec = new mxCodec();
	    String xml;
	    FileOutputStream outputStream = null;
	    try {
		xml = URLEncoder.encode(mxXmlUtils.getXml(codec.encode(graph.getModel())), "UTF-8");
		mxPngEncodeParam param = mxPngEncodeParam.getDefaultEncodeParam(image);
		param.setCompressedText(new String[] { "mxGraphModel", xml });
		// Saves as a PNG file
		String name = diagramController.getDiagram().getName();
		outputStream = new FileOutputStream(new File(name + ".png"));
		mxPngImageEncoder encoder = new mxPngImageEncoder(outputStream,	param);
		if (image != null) {
		    encoder.encode(image);
		} else {
		    JOptionPane.showMessageDialog(graphComponent, mxResources.get("noImageData"));
		}
	    } catch (Exception e1) {
		e1.printStackTrace();
	    } finally {
			try {
			    outputStream.close();
			} catch (IOException e1) {
			    e1.printStackTrace();
			}
	    }
	}
    }

}
